package is.api.demo.rest.server;

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.http.MediaType;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**********************************************************************
 * &lt;p&gt;文件名：RestTemplateUtils.java &lt;/p&gt;
 * &lt;p&gt;文件描述：RestTemplate 工具类5
 * @project_name：ccsinterface
 * @author zengshunyao
 * @create 2023/10/18 16:22
 * @history
 * @department：政务事业部
 * Copyright ChengDu Funi Cloud Code Technology Development CO.,LTD 2014
 *                    All Rights Reserved.
 */
public abstract class RestTemplateUtils {

    //锁
    private static final Object restTemplateHttpLock = new Object();
    private static final Object restTemplateHttpsIgnoreSSLLock = new Object();
    //普通RestTemplate
    private static volatile RestTemplate restTemplateHttp;
    //忽略证书的RestTemplate
    private static volatile RestTemplate restTemplateHttpsIgnoreSSL;

    /**
     * 获取 RestTemplate
     *
     * @return
     */
    public static RestTemplate getRestTemplate(final String url) {
        if (!StringUtils.hasText(url)) {
            throw new IllegalArgumentException("请求地址:[" + url + "]异常");
        }
        //https
        if (StringUtils.startsWithIgnoreCase(url, "https://")) {
            if (restTemplateHttpsIgnoreSSL == null) {
                synchronized (restTemplateHttpsIgnoreSSLLock) {
                    if (restTemplateHttpsIgnoreSSL == null) {
                        restTemplateHttpsIgnoreSSL = restTemplateCCSIgnoreSSL();
                    }
                }
            }
            return restTemplateHttpsIgnoreSSL;
        }

        //http
        if (StringUtils.startsWithIgnoreCase(url, "http://")) {
            if (restTemplateHttp == null) {
                synchronized (restTemplateHttpLock) {
                    if (restTemplateHttp == null) {
                        restTemplateHttp = restTemplateHttp();
                    }
                }
            }
            return restTemplateHttp;
        }
        //无法匹配 http 或 https
        throw new IllegalArgumentException("请求地址:[" + url + "]异常");
    }

    /**
     * 普通restTemplate
     *
     * @return
     */
    private static RestTemplate restTemplateHttp() {
        synchronized (restTemplateHttpLock) {
            final RestTemplate restTemplate = new RestTemplate();
            //中文乱码问题
            final List<HttpMessageConverter<?>> httpMessageConverters = restTemplate.getMessageConverters();
            httpMessageConverters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
            //字符串默认编码 utf-8
            httpMessageConverters
                    .forEach(httpMessageConverter -> {
                        if (httpMessageConverter instanceof StringHttpMessageConverter) {
                            final StringHttpMessageConverter messageConverter = StringHttpMessageConverter.class.cast(httpMessageConverter);
                            {
                                final List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
                                final List<MediaType> newSupportedMediaTypes = new ArrayList<>(supportedMediaTypes);
                                newSupportedMediaTypes.add(MediaType.APPLICATION_JSON);
                                newSupportedMediaTypes.add(MediaType.TEXT_HTML);
                                messageConverter.setSupportedMediaTypes(newSupportedMediaTypes);
                            }
                        }
                    });

            //支持 MediaType.TEXT_PLAIN;MediaType.TEXT_HTML
            //restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());

            //解决TEXT_HTML 无法解析问题
            //final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
            //mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML));
            //restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
            {
                //通过BufferingClientHttpRequestFactory对象包装现有的RequestFactory，用来支持多次调用getBody()方法
                restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory()));
                // 打印记录
                //restTemplate.setInterceptors(Collections.singletonList(new LoggingRequestInterceptor(15000)));
            }

            //设置异常处理
            //restTemplate.setErrorHandler(new MyResponseErrorHandler());
            return restTemplate;
        }
    }

    /**
     * 正常的HttpRequest 工厂
     *
     * @return
     */
    private static ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        //ClientHttpRequestFactory
        //HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        // 连接超时
        //clientHttpRequestFactory.setConnectTimeout(15000);
        // 数据读取超时时间
        //clientHttpRequestFactory.setReadTimeout(15000);
        //连接不够用的等待时间
        //clientHttpRequestFactory.setConnectionRequestTimeout(15000);
        //return clientHttpRequestFactory;

        // 配置http请求的连接超时时间和读取超时时间
        final SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        //连接超时时间;单位：毫秒;2分钟
        factory.setConnectTimeout(2 * 60 * 1000);
        //读取超时时间;单位：毫秒;5分钟
        factory.setReadTimeout(5 * 60 * 1000);

        return factory;
    }

    /**
     * 忽略证书restTemplate
     *
     * @return
     */
    private static RestTemplate restTemplateCCSIgnoreSSL() {
        synchronized (restTemplateHttpsIgnoreSSLLock) {
            //可以忽略https证书不安全的问题
            final RestTemplate restTemplate = new RestTemplate(generateHttpRequestFactory4SSL());
            //中文乱码问题
            final List<HttpMessageConverter<?>> httpMessageConverters = restTemplate.getMessageConverters();
            httpMessageConverters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));

            httpMessageConverters
                    .forEach(httpMessageConverter -> {
                        if (httpMessageConverter instanceof StringHttpMessageConverter) {
                            final StringHttpMessageConverter messageConverter = (StringHttpMessageConverter) httpMessageConverter;
                            //messageConverter.setDefaultCharset(StandardCharsets.UTF_8);
                            {
                                final List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
                                final List<MediaType> newSupportedMediaTypes = new ArrayList<>(supportedMediaTypes);
                                newSupportedMediaTypes.add(MediaType.APPLICATION_JSON);
                                newSupportedMediaTypes.add(MediaType.TEXT_HTML);
                                messageConverter.setSupportedMediaTypes(newSupportedMediaTypes);
                            }
                        }
                    });
            //支持 MediaType.TEXT_PLAIN;MediaType.TEXT_HTML
            //restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());

            //解决TEXT_HTML 无法解析问题
            final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
            mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(org.springframework.http.MediaType.TEXT_HTML));
            restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);

            {
                //通过BufferingClientHttpRequestFactory对象包装现有的RequestFactory，用来支持多次调用getBody()方法
                restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory()));
                // 打印记录
                //restTemplate.setInterceptors(Collections.singletonList(new LoggingRequestInterceptor(15000)));
            }

            //设置异常处理
            //restTemplate.setErrorHandler(new MyResponseErrorHandler());
            return restTemplate;
        }
    }

    /**
     * 忽略https的工厂
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws KeyStoreException
     */
    private static HttpComponentsClientHttpRequestFactory generateHttpRequestFactory4SSL() {
        try {
            final TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
            final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();

            final SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());

            final HttpClientBuilder httpClientBuilder = HttpClients.custom();
            httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);

            final CloseableHttpClient httpClient = httpClientBuilder.build();

            final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
            factory.setHttpClient(httpClient);
            //factory.setConnectTimeout(10 * 1000);
            //factory.setReadTimeout(30 * 1000);
            return factory;
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            System.err.println("创建ignore SSL RestTemplate失败");
            e.printStackTrace();
            throw new RuntimeException("创建ignore SSL RestTemplate失败", e);
        }
    }
}
